package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;
import com.tianji.api.dto.trade.OrderBasicDTO;
import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.*;
import com.tianji.learning.domain.dto.LearningPlanDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author wxh
 * @since 2024-08-20
 */
@Slf4j
@Service
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {


    @Autowired
    private CourseClient courseClient;

    @Autowired
    private CatalogueClient catalogueClient;
    @Autowired
    private LearningRecordMapper recordMapper;


    /**
     * 将课程的数据进行分装
     *
     * @param userId
     * @param courseIds
     */
    @Override
    @Transactional
    public void coruseTime(Long userId, List<Long> courseIds) {
        //通过课程id查询课程信息返回到我的课表中
        //用过课程id查询课程信息
        List<CourseSimpleInfoDTO> simpleInfoLists = courseClient.getSimpleInfoList(courseIds);
        //判断是否为空
        if (ObjectUtil.isEmpty(simpleInfoLists)) {
            log.error("课程不存在，无法添加到我的课表~");
            return;
        }
        //基于CourseClient查询课程的有效期复制到list集合中   //指定集合大小，避免多次扩容
        List<LearningLesson> list = new ArrayList<>(simpleInfoLists.size());
        //进行数据的分装
        for (CourseSimpleInfoDTO simpleInfoList : simpleInfoLists) {
            //向LearningLesson中赋值
            LearningLesson learningLesson = new LearningLesson();
            //获取课程过期时间
            Integer validDuration = simpleInfoList.getValidDuration();
            //判断过期时间是否为空
            if (ObjectUtil.isEmpty(validDuration) || validDuration <= 0) {
                continue;
            }
            ;
            if (validDuration <= 9999) {
                //获取当前系统时间
                LocalDateTime now = LocalDateTime.now();
                //设置过期时间
                learningLesson.setExpireTime(now.plusMonths(validDuration));
            }

            //设置userId
            learningLesson.setUserId(userId);
            //设置课程Id
            learningLesson.setCourseId(simpleInfoList.getId());
            list.add(learningLesson);
        }
        //批量插入到我的课表
        this.saveBatch(list);
    }

    /**
     * 分页查询我的课表
     *
     * @param query
     * @return
     */
    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        //从本地线程获取userID
        Long userId = UserContext.getUser();
        //设置排序字段，没有根据最近时间排序
        Page<LearningLesson> page = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)//构建where条件
                .page(query.toMpPage("latest_learn_time", false));//前端没有排序字段，则按"latest_learn_time"排序
        //判断查询的结果是否为空
        List<LearningLesson> records = page.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            //数据为空的话返回一个空集合
            log.info("返回page的records为空~");
            return new PageDTO<>(page.getTotal(), page.getPages(), Collections.emptyList());
        }
        Map<Long, CourseSimpleInfoDTO> collectMap = courseIdAndcoureseInfo2Map(records);
        //转变为一个map，直接通过键找值的方式，键为课程的id，值为课程的信息
        //创建一个集合接收新的分装数据
        List<LearningLessonVO> learningLessonVos = new ArrayList<>(records.size());
        //不为空进行数据分装
        for (LearningLesson record : records) {
            //对象拷贝
            LearningLessonVO learningLessonVO = BeanUtils.copyBean(record, LearningLessonVO.class);
            //再遍历课程信息集合给learningLessonVO分装数据
            /*for (CourseSimpleInfoDTO coureseInfo : coureseInfos) {
                if (learningLessonVO.getCourseId().equals(coureseInfo.getId())) {
                    //手动分装特定的属性
                    learningLessonVO.setCourseName(coureseInfo.getName());
                    learningLessonVO.setCourseCoverUrl(coureseInfo.getCoverUrl());
                    //最后一次学习的小节
                    learningLessonVO.setSections(coureseInfo.getSectionNum());
                    //将数据添加到集合中
                    learningLessonVos.add(learningLessonVO);
                }
            }*/
            CourseSimpleInfoDTO courseValue = collectMap.get(record.getCourseId());
            learningLessonVO.setCourseName(courseValue.getName());
            learningLessonVO.setCourseCoverUrl(courseValue.getCoverUrl());
            //最后一次学习的小节
            learningLessonVO.setSections(courseValue.getSectionNum());
            //将数据添加到集合中
            learningLessonVos.add(learningLessonVO);

        }
        //返回pageDto对象
        return PageDTO.of(page, learningLessonVos);
    }

    /**
     * 将课程信息转为map集合，
     * key：课程的id，value：课程的信息，后续听过key->value的形式高效循环
     *
     * @param records
     * @return
     */
    private Map<Long, CourseSimpleInfoDTO> courseIdAndcoureseInfo2Map(List<LearningLesson> records) {
        //将课表集合中的课程id收集为一个set集合
        Set<Long> courseIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());

        //远程调用根据set集合的中课程的id去查询课程的全部信息
        List<CourseSimpleInfoDTO> courseInfo = courseClient.getSimpleInfoList(courseIds);
        //再转为一个Map集合
        Map<Long, CourseSimpleInfoDTO> collect = courseInfo.stream().collect(Collectors.toMap(CourseSimpleInfoDTO::getId, Function.identity()));
        //返回Map，供后续高效使用，可以直接通过courseId拿到课程信息
        return collect;
    }


    /**
     * 展示最近一次学习课程
     *
     * @return
     */
    @Override
    public LearningLessonVO selectLerningLesson() {
        //获取当前用户的Id
        Long userId = UserContext.getUser();
        //查询数据库获取最后一次学习课程的信息
        LearningLesson learningLesson = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (ObjectUtil.isEmpty(learningLesson)) {
            return null;
        }
        //对象拷贝
        LearningLessonVO learningLessonVO = BeanUtils.copyBean(learningLesson, LearningLessonVO.class);

        //远程调用openfeigin根据当前课程的id查询这门课程的全部信息
        CourseFullInfoDTO courseInfo =
                courseClient.getCourseInfoById(learningLesson.getCourseId(), false, false);
        //判断最近一次学习的课程是否为空
        if (ObjectUtil.isEmpty(courseInfo)) {
            throw new BadRequestException("最近一次学习信息为空~");
        }
        //根据用户id查询查询课程总数
        Integer count = this.lambdaQuery().eq(LearningLesson::getUserId, userId).count();

        //数据分装
        learningLessonVO.setCourseName(courseInfo.getName());
        learningLessonVO.setCourseCoverUrl(courseInfo.getCoverUrl());
        learningLessonVO.setSections(courseInfo.getSectionNum());
        learningLessonVO.setCourseAmount(count);

        //查询小节数
        List<CataSimpleInfoDTO> catalogues =
                catalogueClient.batchQueryCatalogue(CollUtils.singletonList(learningLesson.getLatestSectionId()));
        if (ObjectUtil.isNotEmpty(catalogues)) {
            CataSimpleInfoDTO cataSimpleInfoDTO = catalogues.get(0);
            //设置最近一次学习的小节名称
            learningLessonVO.setLatestSectionName(cataSimpleInfoDTO.getName());
            //设置最近一次学习的小节数
            learningLessonVO.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());
        }
        //返回数据
        return learningLessonVO;
    }


    /**
     * 删除课程（已经失效或者退款）
     *
     * @param courseId
     */
    @Override
    public void deleteCourse(Long courseId) {
        //获取当前用户的id
        Long userId = UserContext.getUser();
        //根据课程id去数据库中删除 ，判断状态是否已经失效
        this.lambdaUpdate()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId)
                .eq(LearningLesson::getStatus, LessonStatus.EXPIRED.getValue())
                .remove();
    }

    /**
     * 监测到退款的订单，进行退款
     *
     * @param orderBasicDTO
     */
    @Transactional
    @Override
    public void LessonRefund(OrderBasicDTO orderBasicDTO) {
        //获取当前用户的id
        Long userId = UserContext.getUser();
        //删除用户退款的订单课程
        //监听到mq发的消息，删除该退款课程
        this.lambdaUpdate().eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, orderBasicDTO.getOrderId())
                .remove();

    }

    /**
     * 查询课程是否有效
     */
    @Override
    public Long selectCourseValid(Long courseId) {
        // 1.获取登录用户
        Long userId = UserContext.getUser();
        if (userId == null) {
            return null;
        }
        // 2.查询课程
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        if (lesson == null || LessonStatus.EXPIRED.equals(lesson.getStatus())) {
            return null;
        }
        return lesson.getId();

    }

    /**
     * 查询用户课表中指定课程的信息
     *
     * @param courseId
     */
    @Override
    public LearningLessonVO selectIsCourseValid(Long courseId) {
        //获取本地线程id
        Long userId = UserContext.getUser();
        //查询当前课程的信息
        LambdaQueryChainWrapper<LearningLesson> courseInfo = this.lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        if (ObjectUtil.isEmpty(courseInfo)) {
            throw new BizIllegalException("查询的指定课程信息为空");
        }
        //对象拷贝
        return BeanUtils.copyBean(courseInfo, LearningLessonVO.class);
    }

    /**
     * 统计课程学习人数
     *
     * @param courseId 课程id
     * @return 学习人数
     */
    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
        Integer learingLessonCount = this.lambdaQuery()
                .eq(LearningLesson::getCourseId, courseId)
                .in(LearningLesson::getStatus,
                        LessonStatus.NOT_BEGIN,
                        LessonStatus.LEARNING,
                        LessonStatus.FINISHED).count();
        return learingLessonCount;
    }


    /**
     * 查询课程记录的课表信息
     *
     * @param courseId
     * @return
     */
    @Override
    public LearningLesson queryCourseInfoByCourseId(Long courseId, Long userId) {
        return this.getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    /**
     * 7.新增学习计划
     *
     * @param learningPlanDTO
     */
    @Override
    public void addLearningPlan(LearningPlanDTO learningPlanDTO) {
        //先查询我的课表是否有该课程
        LearningLesson learningLesson =
                getOne(buildUserIdAndCourseIdWrapper(UserContext.getUser(), learningPlanDTO.getCourseId()));
        AssertUtils.isNotNull(learningLesson, "课程信息不存在");
        this.lambdaUpdate()
                .set(learningLesson.getPlanStatus() ==
                        PlanStatus.NO_PLAN, LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .set(LearningLesson::getWeekFreq, learningPlanDTO.getFreq())
                .eq(LearningLesson::getId, learningLesson.getId())
                .update();
    }

    /**
     * 8.查询我的学习计划
     *
     * @param pageQuery
     * @return
     */
    @Override
    public LearningPlanPageVO queryMyLearningPlan(PageQuery pageQuery) {
        LearningPlanPageVO vo = new LearningPlanPageVO();
        //1.分页查询我的课表
        //select * from learning_lesson where user_id = 2 and plan_status = 1 and status in (0,1) order by latest_learn_time desc
        Page<LearningLesson> learningPage = this.lambdaQuery()
                .eq(LearningLesson::getUserId, UserContext.getUser())
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(pageQuery.toMpPage("latest_learn_time", false));
        //1.1如果为空则返回一个空集合，里面带有totle和pages
        List<LearningLesson> records = learningPage.getRecords();
        if (ObjectUtil.isEmpty(records)) {
            return vo.emptyPage(learningPage);
        }
        //2.查询课程表拿到课程名称和课程的总小节数
        Map<Long, CourseSimpleInfoDTO> courseInfos = courseIdAndcoureseInfo2Map(records);
        //2.1创建LearningPlanVO集合
        List<LearningPlanVO> learningPlanVOS = new ArrayList<>(records.size());
        //3.查询本周每门课对应完成的小节数
        /*select r.lesson_id AS id,count(*) AS num from learning_record r
        where r.user_id = 129 and r.finished = 1 and r.finish_time
        between '2024-08-19 15:37:50' and '2024-08-25 15:37:50' group by lesson_id;*/
        LocalDateTime begin = DateUtils.getWeekBeginTime(LocalDate.now());
        LocalDateTime end = DateUtils.getWeekEndTime(LocalDate.now());
        //3.1构建查询条件
        QueryWrapper<LearningRecord> queryWrapper = new QueryWrapper();
        queryWrapper.select("lesson_id AS id,count(*) AS num")
                .eq("user_id", UserContext.getUser())
                .eq("finished", true)
                .between("finish_time", begin, end)
                .groupBy("lesson_id");
        //3.2现在的list里面是一个一个的map，key为id value为id对应的值 key为num，value为 num对应的值（3）
        List<Map<String, Object>> maps = recordMapper.selectMaps(queryWrapper);
        //3.3将maps转变为能通过key拿到对应的id的值.
        // 简单来说就是拿到旧的map的key的值（key：id，value：123456）(key：num，value：3)
        // 为新的map的key对应的值key：(id)123456，value：(num)3
        Map<Long, Long> courseKeyToNum = maps.stream().collect(Collectors.toMap(c -> (Long) c.get("id"), e -> (Long) e.get("num")));
        //4.对分页结果records进行数据分分装
        for (LearningLesson record : records) {
            //4.1对象拷贝
            LearningPlanVO learningPlanVO = BeanUtils.copyBean(record, LearningPlanVO.class);

            //4.2通过课程id拿到对应的课程信息
            CourseSimpleInfoDTO courseInfo = courseInfos.get(record.getCourseId());
            if (ObjectUtil.isNotEmpty(courseInfo)) {
                learningPlanVO.setCourseName(courseInfo.getName());
                learningPlanVO.setSections(courseInfo.getSectionNum());
            }
            //4.3每周学习的小节数
            Integer intValue = courseKeyToNum.getOrDefault(record.getId(), 0L).intValue();
            learningPlanVO.setWeekLearnedSections(intValue);
            learningPlanVOS.add(learningPlanVO);
        }
        //5.本周计划学习的的小节数量
        //select sum(week_freq) from learning_lesson where user_id = ? and plan_status = 1 and status in (0,1);
        LearningLesson lesson = this.query()
                .select("sum(week_freq) as total_weekfreq")
                .eq("user_id", UserContext.getUser())
                .eq("plan_status", PlanStatus.PLAN_RUNNING)
                .in("status", LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .one();

        //6.本周已学习的小节数量
        long sum = courseKeyToNum.values().stream().mapToLong(l -> l.longValue()).sum();
        Integer weekFinished = Long.valueOf(sum).intValue();
        //7.数据分装，本周计划的总小节数，本周完成的小节数，积分
        vo.setWeekTotalPlan(lesson.getTotalWeekfreq());
        vo.setWeekFinished(weekFinished);
        vo.setWeekPoints(null);// TODO 本周学习积分
        //8.返回Vo
        return vo.pageInfo(learningPage.getTotal(), learningPage.getPages(), learningPlanVOS);
    }

    /**
     * 根据课程id与用户id查询我的课表相关信息方法
     *
     * @param userId
     * @param courseId
     * @return
     */
    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }

}
